/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
 */

import { hvigorCore, Project } from '@ohos/hvigor';


import { ApiVersion } from '../const/sdk-const.js';
import { ApiVersionHandler } from '../distribution/api-version-handler.js';
import { DistroFilterConfig, DistroFilterHandler } from '../distribution/distro-filter-handler.js';
import { DistroFilterValidateEntrance } from '../distribution/distro-filter-validate-entrance.js';
import { ScreenShapeHandler } from '../distribution/screen-shape-handler.js';
import { ScreenWindowHandler } from '../distribution/screen-window-handler.js';
import { ProjectModel } from '../model/project/project-model.js';
import { checkIntersection } from '../utils/array-util.js';
import { OhosLogger } from '../utils/log/ohos-logger.js';
import { hvigorOrToolChainsChanged } from '../utils/meta-util.js';
import { TaskNames } from './common/task-names.js';
import { ProjectTaskService } from './service/project-task-service.js';
import { OhosAppTask } from './task/ohos-app-task.js';

import CommonTask = TaskNames.CommonTask;

const _log = OhosLogger.getLogger('hvigor-preBuildApp');

/**
 * 执行app打包相关任务前，需要进行一些预校验或者准备工作
 *
 * @since 2022/6/17
 */
export class PreBuildApp extends OhosAppTask {
  constructor(project: Project, taskService: ProjectTaskService) {
    super(project, taskService, CommonTask.PRE_BUILD_APP);
  }

  protected doTaskAction(): void {
    this.checkConfigModuleStatus();

    // api8 FA工程新增distroFilter校验
    if (this.isApi8Fa(this.service)) {
      this.validateDistroFilter(this.service.getProjectModel());
    }
  }

  /**
   * 为了防止有多个入口影响打包app的逻辑，需要增加该校验
   * 1.打包app只从项目的根build-profile.json中确认，不允许在命令行中再通过-p module指定Module和Target
   * 2.在build-profile.json5中至少有一个模块的一个target会执行打包hap
   *
   * @private
   */
  private checkConfigModuleStatus() {
    const configModules = hvigorCore.getExtraConfig().get('module');

    if (configModules !== undefined) {
      const moduleNameList = this.service.getProjectModel().getModuleSpecificTargets().keys();
      for (const moduleName of moduleNameList) {
        const moduleImpl = this.service.getProjectModel().getModuleModelByName(moduleName);
        if (moduleImpl && !moduleImpl.isHarModule()) {
          _log.printErrorExit('CANNOT_CONFIGURE_MODULE_WHEN_PACKING', [moduleName]);
        }
      }
    }

    if (this.service.getProductDataMap().size === 0) {
      const targetProductName = this.service.getTargetProduct().name;
      _log.printErrorExit('NO_HAP_FOUND', [this.project.getBuildProfilePath()], [[targetProductName]]);
    }
  }

  /**
   * distroFilter校验
   * 1.多entry设备类型存在交集场景
   * 2.target定制化场景
   * @param projectModel
   * @private
   */
  private validateDistroFilter(projectModel: ProjectModel): void {
    if (DistroFilterHandler.validateMultiTarget(projectModel)) {
      this.processTargetDistroFilter(projectModel);
    } else if (DistroFilterHandler.validateMultiEntry(projectModel)) {
      this.processEntryDistroFilter(projectModel);
    }
  }

  /**
   * 判断当前工程是否为api8 FA模型
   * @param projectTaskService ProjectTaskService
   * @private
   */
  private isApi8Fa(projectTaskService: ProjectTaskService) {
    const product = projectTaskService.getTargetProduct();
    const compileSdkVersion = projectTaskService.getProjectModel().getProductApiMeta(product.name)?.compileSdkVersion;
    return projectTaskService.isFaMode() && compileSdkVersion?.version === ApiVersion.API_VERSION_8;
  }

  /**
   * 以target维度对build-profile.json5中的config-distroFilter进行校验
   * @param projectModel
   */
  public processTargetDistroFilter(projectModel: ProjectModel): void {
    let distroFilterConfigList: DistroFilterConfig[] = [];
    projectModel.getAllEntryModules().forEach((sourceEntry) => {
      const sourceEntryExecutableTargetList = DistroFilterHandler.getCurModuleExecutableTargets(projectModel, sourceEntry);
      if (
        sourceEntryExecutableTargetList.length > 1 &&
        DistroFilterHandler.checkSingleEntryTargetDeviceType(projectModel, sourceEntry, sourceEntryExecutableTargetList)
      ) {
        // 若当前sourceEntry的可执行target数>1,则需要判断是否进行distroFilter校验;<=1的情况不会出现在此处逻辑
        sourceEntryExecutableTargetList.forEach((sourceEntryExecutableTarget) => {
          const sourceEntryTargetDistroFilterConfig = DistroFilterHandler.getTargetDistroFilterConfig(projectModel, sourceEntry, sourceEntryExecutableTarget);
          distroFilterConfigList.push(sourceEntryTargetDistroFilterConfig);
        });

        // 发送校验请求 并清空当前循环保存的distroFilterConfig集合
        this.sendDistroFilterRequest(projectModel, distroFilterConfigList);
        distroFilterConfigList = [];
      }

      // 若当前sourceEntry存在与之设备类型相交的targetEntry,则需要对设备类型相交模块存在的所有可执行target进行校验
      const intersectedEntryList = DistroFilterHandler.findIntersectedEntryList(projectModel, sourceEntry);
      intersectedEntryList.forEach((intersectedEntry) => {
        // 获取与sourceEntry设备类型存在交集的entry的可执行target集合
        const intersectedEntryExecutableTargetList = DistroFilterHandler.getCurModuleExecutableTargets(projectModel, intersectedEntry);

        // 获取sourceEntry和targetEntry模块下可执行的target并进行交集判断
        sourceEntryExecutableTargetList.forEach((sourceEntryExecutableTarget) => {
          intersectedEntryExecutableTargetList.forEach((intersectedEntryExecutableTarget) => {
            const sourceEntryDeviceTypeList = DistroFilterHandler.getTargetDeviceType(projectModel, sourceEntry, sourceEntryExecutableTarget);
            const intersectedEntryDeviceTypeList = DistroFilterHandler.getTargetDeviceType(projectModel, intersectedEntry, intersectedEntryExecutableTarget);

            if (checkIntersection(sourceEntryDeviceTypeList, intersectedEntryDeviceTypeList)) {
              // 若模块各自可执行的target存在定制设备类型且存在交集,则只需要对这些target进行distroFilter校验
              const sourceEntryTargetDistroFilterConfig = DistroFilterHandler.getTargetDistroFilterConfig(
                projectModel,
                sourceEntry,
                sourceEntryExecutableTarget,
              );
              const intersectedEntryTargetDistroFilterConfig = DistroFilterHandler.getTargetDistroFilterConfig(
                projectModel,
                intersectedEntry,
                intersectedEntryExecutableTarget,
              );
              distroFilterConfigList.push(sourceEntryTargetDistroFilterConfig);
              distroFilterConfigList.push(intersectedEntryTargetDistroFilterConfig);
            }

            // 发送校验请求 并清空当前循环保存的distroFilterConfig集合
            this.sendDistroFilterRequest(projectModel, distroFilterConfigList);
            distroFilterConfigList = [];
          });
        });
      });
    });
  }

  /**
   * 以模块维度仅在多entry且设备类型存在交集时对config.json中的distroFilter进行校验
   * @param projectModel
   */
  public processEntryDistroFilter(projectModel: ProjectModel): void {
    projectModel.getAllEntryModules().forEach((sourceEntry) => {
      // 获取sourceEntry模块设备类型存在交集的targetEntry模块集合
      const sourceEntryExecutableTarget = DistroFilterHandler.getCurModuleExecutableTargets(projectModel, sourceEntry)[0];
      const sourceEntryTargetDeviceTypeList = DistroFilterHandler.getTargetDeviceType(projectModel, sourceEntry, sourceEntryExecutableTarget);
      let distroFilterConfigList: DistroFilterConfig[] = [];
      const intersectedEntryList = DistroFilterHandler.findIntersectedEntryList(projectModel, sourceEntry);
      if (intersectedEntryList.length === 0) {
        return;
      }
      intersectedEntryList.forEach((intersectedEntry) => {
        const intersectedEntryExecutableTarget = DistroFilterHandler.getCurModuleExecutableTargets(projectModel, intersectedEntry)[0];
        const intersectedEntryTargetDeviceTypeList = DistroFilterHandler.getTargetDeviceType(projectModel, intersectedEntry, intersectedEntryExecutableTarget);
        if (checkIntersection(sourceEntryTargetDeviceTypeList, intersectedEntryTargetDeviceTypeList)) {
          // 遍历targetEntryList,将sourceEntry及其类型的存在交集的targetEntry传入distroFilterConfigList,传递给校验链处理
          const sourceDistroFilterConfig = DistroFilterHandler.getFADistroFilterConfig(projectModel, sourceEntry);
          if (sourceDistroFilterConfig) {
            distroFilterConfigList.push(sourceDistroFilterConfig);
          }
          const targetDistroFilterConfig = DistroFilterHandler.getFADistroFilterConfig(projectModel, intersectedEntry);
          if (targetDistroFilterConfig) {
            distroFilterConfigList.push(targetDistroFilterConfig);
          }

          // 发送校验请求 并清空当前循环保存的distroFilterConfig集合
          this.sendDistroFilterRequest(projectModel, distroFilterConfigList);
          distroFilterConfigList = [];
        }
      });
    });
  }

  /**
   * 传递distroFilter校验链请求
   * @param projectModel
   * @param distroFilterConfigList distroFilter配置集合
   * @private
   */
  public sendDistroFilterRequest(projectModel: ProjectModel, distroFilterConfigList: DistroFilterConfig[]) {
    const distributionValidateEntrance = new DistroFilterValidateEntrance(projectModel);

    // 传递校验无顺序要求,若之后需要增加其他校验维度添加对应handler即可
    const apiVersionHandler = new ApiVersionHandler(projectModel);
    const screenShapeHandler = new ScreenShapeHandler(projectModel);
    const screenWindowHandler = new ScreenWindowHandler(projectModel);

    distributionValidateEntrance.setNextDimensionHandler(apiVersionHandler);
    apiVersionHandler.setNextDimensionHandler(screenShapeHandler);
    screenShapeHandler.setNextDimensionHandler(screenWindowHandler);

    distributionValidateEntrance.executeValidate(distroFilterConfigList);
  }

  initTaskDepends() {
    hvigorOrToolChainsChanged(this.service.getSdkInfo()) && this.dependsOn(CommonTask.CLEAN.name);
  }
}
